#!/usr/bin/env python
#
# Copyright 2022 Huaqin Technology. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#    * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
#    * Neither the name of Huaqin Technology nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Does tws codestyle check on c files.

The goal of this script is to identify places in the code that "may" be in
non-compliance with BD1 coding style. It does not attempt to fix up these
problems -- the point is to educate. It does also not attempt to find all
problems, or to ensure that everything it does find is legitimately a
problem.
"""

import sys
import re

# & 'C:\Program Files\Python37\python.exe' -m pydoc -w .\tws_c_codestyle_v0.3.py
_USAGE = """
Synatax: xxx.py <file> [files]
"""

# The test files for this script.
_test_files = None
# _test_files = ['./test_code/ux_manager.c', './test_code/ux_manager.h']

# The valid extentions for filename
_valid_extentions = ['c', 'h']
_valid_extentions_re_pattern = r'[ch]'

# lines加不加前后空行, 对每次计算rang有影响, 使用宏定义消除影响
_LINE_LENS_RANGE_START = 0 # 给range函数使用, 起始行
_LINE_LENS_RANGE_STOP = 0 # 给range函数结束使用, 结束行
_LINE_LENS_TRUE = 0 # lines的真实行数
_LINE_LENS_WITH_MARKER = 0 # lines加上了前后MARKER的行数

# 每一行的最大长度
_MAX_LINE_LENS = 80

class _TWSLintState(object):
  """Maintains code checker states"""
  def __init__(self) -> None:
    self.error_count = 0
  
  def ResetErrorCounts(self):
    self.error_count = 0


_clint_state = _TWSLintState()


def _PrintUsage(message):
  """Print a brief usage string and exits, optionally with an error message.

  Args:
    message: The optional error message.
  """
  print(_USAGE)
  print('The valid file extentions for file name is:')
  print(_valid_extentions)

  if message:
    sys.exit('\nFatal _Error: ' + message)
  else:
    sys.exit(1)


_ERROR_CATEGORY = [
  # 'whitespace/filename',
  # 'legal/copyright',
  # 'whitespace/hash_if_format',
  # 'whitespace/hash_define_format',
  # 'whitespace/eof_newline',
  # 'whitespace/line_length',
  # 'whitespace/unused_space',
  # 'whitespace/illegal_tab',
  # 'whitespace/language',
  # 'whitespace/include',
  # 'whitespace/header_file',
  # 'whitespace/c_file',
  # 'whitespace/sigle_line_comment',
  # 'whitespace/multi_line_comment',
  # 'runtime/invalid_increment',
  # 'whitespace/redundant_space',
  # 'struct/comma',
  # 'struct/semicolon',
  # 'struct/brackets',
  # 'struct/indentation',
  # 'struct/brace',
  # 'struct/name',
  # 'enum/comma',
  # 'enum/semicolon',
  # 'enum/brackets',
  # 'enum/indentation',
  # 'enum/brace',
  # 'enum/name',
  # 'enum/operator',
  # 'enum/member',
  'function/brace',
  'function/indentation',
  'function/brackets',
  'function/if_else',
  'function/for',
  'function/switch_case',
  'function/comma_semicolon',
  'function/do_while',
  'function/indentation',
]

def _ShouldPrintError(filename, linenum, category, confidence):
  """If we should print the ERROR.
    filter by filename&linenum / category/ confidence.
  """
  if category not in _ERROR_CATEGORY:
    return False

  return True


def _Error(filename, linenum, category, confidence, message):
  """Logs the error we have found

  Args:
    filename: The name of the file containing the error.
    linenum: The number of the line containing the error.
    category: A string used to describe the error.
    confidence: A number from 1-5 representing a confidence score
      for the error. 
      5: We are certain of the problem.
      1: It could be a legitimate construct.
  """
  if _ShouldPrintError(filename, linenum, category, confidence):
    print('%s:%s:  [%s]  [%d]  %s' % (
            filename, linenum, category, confidence, message))


_MIN_LINE_COUNT = 3 # 定义
def _CheckFileNotEmpty(lines):
  """根据文件中行的数量, 确定文件是否为空.
  文件最小行数由"_MIN_LINE_COUNT"定义, 当小于这个行数时, 判定文件为空.
  
  Args: 
    lines: 文件中所有的行列表.
  """
  
  if lines.count <= _MIN_LINE_COUNT:
    return False
  else:
    return True


def _GetFileNameWithoutPath(filename):
  """去掉文件名中的路径, 并返回完整的文件名.

  Args:
    filename: 需要处理的文件名(包含完整路径)

  Returns:
    不带路径的文件名.
  """
  name = re.sub(r'\\', '/', filename) # 将反斜杠'\' 先替换为'/', 便于split处理.
  name = re.split(r'/', name)[-1] # 使用'/'分割字符串, 并取出最后一个元素（文件名）.
  if not name:
    _PrintUsage('filename error' + filename)

  return name


def _CountLeftSpace(line):
  """计算字符串左侧的空格数量, 并返回空格数量
  只计算左侧字符串, 用于比较两行的缩进是否符合规范.

  Args:
    line: 需要计算左侧空格数量的字符串.

  Returns: 
    字符串左侧的空格数量.
  """
  return len(line) - len(line.lstrip())



"""XXX_CONSTRUCT_INFO
  enum, function, struct, union等定义的结构信息
  type: list[{dict}, {dict}, {dict}]
  {
    'line_start':     int   # The start line num of the construct, sometimes before the brackets start line.
    'brackets_start': int   # The start line num of the brackets.
    'line_stop':      int   # The end line num of the construct.
    'depth':          int   # The max depth of the construct, represent how many '{}' it has.
    'data':           list  # The data in line of the whole construct.
  }
"""
_FUNCTION_CONSTRUCT_INFO_LABEL = 'fuction'
_FUNCTION_CONSTRUCT_INFO = [] # function definition info

_ENUM_CONSTRUCT_INFO_LABEL = 'enum'
_ENUM_CONSTRUCT_INFO = [] # enum definition info

_STRUCT_CONSTRUCT_INFO_LABEL = 'struct'
_STRUCT_CONSTRUCT_INFO = [] # struct definition info

_UNION_CONSTRUCT_INFO_LABEL = 'union'
_UNION_CONSTRUCT_INFO = [] # union definition info

_HASH_IF_CONSTRUCT_INFO_LABEL = '#if'
_HASH_IF_CONSTRUCT_INFO = [] # #if/#elif/#else/#endif info

_HASH_DEFINE_CONSTRUCT_INFO_LABEL = '#define'
_HASH_DEFINE_CONSTRUCT_INFO = [] # '#define' line info

_INCLUDE_CONSTRUCT_INFO_LABEL = '#include'
_INCLUDE_CONSTRUCT_INFO = [] # #include info

_VARIABLE_DEFINE_INFO_LABEL = 'array[]='
_VARIABLE_DEFINE_INFO = [] # 变量定义, 普通变量, 结构体变量, 数组变量

_FUNCTION_STATEMENT_CONSTRUCT_INFO_LABEL = 'function_statement'
_FUNCTION_STATEMENT_CONSTRUCT_INFO = [] # 函数声明，非定义


def _SaveConstructInfoAndReplaceLineWithBlank(construct_info, lines, start_line, brackets_start_line, stop_line, depth):
  """按格式保存enum, fuction, union,  struct等结构的信息

  XXX_CONSTRUCT_INFO
    enum, function, struct, union等定义的结构信息
    type: list[{dict}, {dict}, {dict}]
    {
      'line_start':     int   # The start line num of the construct, sometimes before the brackets start line.
      'brackets_start': int   # The start line num of the brackets.
      'line_stop':      int   # The end line num of the construct.
      'depth':          int   # The max depth of the construct, represent how many '{}' it has.
      'data':           list  # The data in line of the whole construct.
    }

  Args:
    construct_info: list, 指向实际的construct列表, 如_FUNCTION_CONSTRUCT_INFO等
    lines: list, 当前文件中所有行的信息(已经处理过的, 如去掉了注释, 字符串等)
    start_line: int, 整体结构的起始行数, 一般与"{"在同一行或者上一行. 
    brackets_start_line: int, "{"的开始行数
    stop_line: int, 整体结构的结束行
    depth: int, 整体结构的'{}'嵌套深度
  """
  construct_data = lines[start_line : stop_line + 1]
  construct_dict = {
    "line_start": start_line,
    "brackets_start": brackets_start_line,
    "line_stop": stop_line,
    "depth": depth,
    "data": construct_data,
  }
  # construct_info.append((start_line, brackets_start_line, stop_line, depth, construct_data))
  construct_info.append(construct_dict)
  for line_num in range(start_line, stop_line + 1): # 已找到的construct信息已经保存, 之后基于保存的信息继续处理, 替换原始lines中对应位置为空, 不影响行数量. 
    lines[line_num] = ''


def _FindMultiLineWithBackslash(lines, start_line, stop_line):
  """寻找以反斜杠'\\'为多行的块

  Args:
    lines: 所有行的list
    start_line: 查找起始行
    stop_line: 查找结束行  
  """
  if not lines[start_line].strip(): # 第一行为空, 直接返回
    return None
  for line_num in range(start_line, stop_line + 1):
    if lines[line_num].strip(): # 使用list[-1]时, 此行不能为空
      if lines[line_num].strip()[-1] != '\\': # 此行不为空, 且最后一个字符不是'\\'时, 说明多行结束
        return line_num
    else: # 此行为空, 说明书写格式有问题，上一行已经结束
      return line_num - 1

  return start_line


def _FindOneBracketsConstruct(construct_info, lines, linenum, label) -> int:
  """基于开始行, 整个construct行信息. 

  Args:
    construct_info: list, 指向实际的construct列表, 如_FUNCTION_CONSTRUCT_INFO等
    lines: list, 当前文件中所有行的信息(已经处理过的, 如去掉了注释, 字符串等)
    linenum: int, 整体结构的起始行数, 也就是发现enum, function, struct, union的起始行.
  """
  # Notes: 结构定义可能有多行模式, 如：
  # static void PowerStateDischargeHeart(POWER_COMMONDATA_T *data, POWER_EDGE_DETECT_HANDLE_T *ocd, \
  #                               POWER_EDGE_DETECT_HANDLE_T *uvd, POWER_EDGE_DETECT_HANDLE_T *uv,\
  #                               POWER_EDGE_DETECT_HANDLE_T *uc)
  # {
  _FIND_ALL_BRACKETS_PATTERN = re.compile(r'[{}]')
  _LINE_STOP_PATTERN = re.compile(r';\s*$')
  _PARENTHESIS_START_PATTERN = re.compile(r'\(')
  _PARENTHESIS_STOP_PATTERN = re.compile(r'\)')
  _PARENTHESIS_PATTERN = re.compile(r'[()]')
  first_line = lines[linenum]
  brackets_start_line = None

  if label == _FUNCTION_CONSTRUCT_INFO_LABEL: # 函数声明单独处理
    if re.match(r'^\s*typedef', first_line):
      return 1
    if len(_PARENTHESIS_START_PATTERN.findall(first_line)) > len(_PARENTHESIS_STOP_PATTERN.findall(first_line)): # 函数"()"中参数列表为多行模式
      start_line = linenum
      stop_line = 0
      depth = 0
      for i in range(start_line, _LINE_LENS_RANGE_STOP):
        for iter in _PARENTHESIS_PATTERN.finditer(lines[i]): # 需要计算'('和')'的个数。
          if iter.group() == '(':
            depth += 1
          elif iter.group() == ')':
            depth -= 1
          if depth == 0: # 找到了完整的参数列表
            stop_line = i
            break
        if stop_line != 0: # 当找到了完整的参数列表, 判断是否为函数声明
          if lines[stop_line].strip()[-1] == ';':
            _SaveConstructInfoAndReplaceLineWithBlank(_FUNCTION_STATEMENT_CONSTRUCT_INFO, lines, start_line, start_line, stop_line, 0)
            return stop_line - start_line + 1
          else:
            break
    elif lines[linenum].strip()[-1] == ';':
      start_line = linenum
      stop_line = linenum
      _SaveConstructInfoAndReplaceLineWithBlank(_FUNCTION_STATEMENT_CONSTRUCT_INFO, lines, start_line, start_line, stop_line, 0)
      return stop_line - start_line + 1
  if label == _VARIABLE_DEFINE_INFO_LABEL:
    if lines[linenum].strip()[-1] == ';':
      start_line = linenum
      stop_line = linenum
      _SaveConstructInfoAndReplaceLineWithBlank(_VARIABLE_DEFINE_INFO, lines, start_line, start_line, stop_line, 0)
      return stop_line - start_line + 1

  if (not _FIND_ALL_BRACKETS_PATTERN.search(first_line)) and _LINE_STOP_PATTERN.search(first_line): # 第一行没有'{'或'}'符号, 且末尾有';', 说明此行是变量定义
    return 1

  depth = 0
  max_depth = 0
  for line_num in range(linenum, _LINE_LENS_RANGE_STOP):
    line = lines[line_num]
    if _FIND_ALL_BRACKETS_PATTERN.search(line):
      if not brackets_start_line:
        brackets_start_line = line_num
      for iter in _FIND_ALL_BRACKETS_PATTERN.finditer(line):
        if iter.group() == '{':
          depth += 1
          max_depth += 1
        elif iter.group() == '}':
          if depth > 1:
            depth -= 1
          else:
            stop_line = line_num
            # construct_lines = lines[linenum: stop_line + 1]
            # construct_info.append((linenum, start_line, stop_line, max_depth, construct_lines))
            _SaveConstructInfoAndReplaceLineWithBlank(construct_info, lines, linenum, brackets_start_line, stop_line, max_depth)
            return (stop_line - linenum + 1)

  # _FIND_ALL_BRACKETS_PATTERN = re.compile(r'[{}]')
  # _BRACKETS_IN_ONE_LINE_PATTERN = re.compile(r'{.*}')
  # _BRACKETS_START_PATTERN = re.compile(r'{')
  # _BRACKETS_STOP_PATTERN = re.compile(r'}')
  # _LINE_STOP_PATTERN = re.compile(r';\s*$')
  # _BRACKETS_IN_SINGLE_LINE_MODE_PATTERN = re.compile(r'') #
  # first_line = lines[linenum]
  # second_line = lines[linenum + 1]
  # start_line = None
  # depth = 0
  # max_depth = 0
  # if _BRACKETS_IN_ONE_LINE_PATTERN.search(first_line): # '{}'同在第1行, 检查了1行
  #   # construct_lines = lines[linenum: linenum + 1]
  #   # construct_info.append((linenum, linenum, linenum, 1, construct_lines))
  #   _SaveConstructInfoAndReplaceLineWithBlank(construct_info, lines, linenum, linenum, linenum, 1)
  #   return 1
  # if (not _BRACKETS_IN_ONE_LINE_PATTERN.search(first_line)) and _LINE_STOP_PATTERN.search(first_line): # 第一行没有'{}', 但有';', 说明不是一个类型定义或结构, 检查了1行
  #   return 1 # 先检查完第一行, 再检查第二行
  # if _BRACKETS_IN_ONE_LINE_PATTERN.search(second_line): # '{}'同在第2行, 检查了1行
  #   # construct_lines = lines[linenum: linenum + 2]
  #   # construct_info.append((linenum, linenum + 1, linenum + 1, 1, construct_lines))
  #   _SaveConstructInfoAndReplaceLineWithBlank(construct_info, lines, linenum, linenum + 1, linenum + 1, depth)
  #   return 2

  # if _BRACKETS_START_PATTERN.search(first_line): # '{'在第一行
  #   start_line = linenum
  #   # depth = 1
  # elif _BRACKETS_START_PATTERN.search(second_line): # '{'在第二行
  #   start_line = linenum + 1
  #   # depth = 1
  # else: # 不是一个'{}'类型定义或结构, 因为第一行和第二行都没有, 检查了1行
  #   return 1

  # if start_line:
  #   depth = 1
  #   max_depth = 1
  #   for line_num in range(start_line + 1, _LINE_LENS_RANGE_STOP):
  #     line = lines[line_num]
  #     if _FIND_ALL_BRACKETS_PATTERN.search(line):
  #       for iter in _FIND_ALL_BRACKETS_PATTERN.finditer(line):
  #         if iter.group() == '{':
  #           depth += 1
  #           max_depth += 1
  #         elif iter.group() == '}':
  #           if depth > 1:
  #             depth -= 1
  #           else:
  #             stop_line = line_num
  #             # construct_lines = lines[linenum: stop_line + 1]
  #             # construct_info.append((linenum, start_line, stop_line, max_depth, construct_lines))
  #             _SaveConstructInfoAndReplaceLineWithBlank(construct_info, lines, linenum, start_line, stop_line, max_depth)
  #             return (stop_line - linenum + 1)

  return 1


def GuardCheckForFilenameFormat(filename, file_extention, lines):
  """检查文件命名规范.
  1. 名字全部使用英文小写字母a-z, 中间可以有下划线'_'.
  2. 除了文件类型后缀'.c''.h', 文件名中不能有其他的'.'符号.
  3. 不能使用'.'或'_开头', 只能使用英文字母开头.

  Errors: 
    category: [whitespace/filename]
    confidence: [5]
    message: [Filename format error: "xxx_xxx.h"]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  _FILE_NAME_FORMAT_PATTERN = re.compile(r'^[a-z][a-z_]*.' + _valid_extentions_re_pattern + r'$')
  if not _FILE_NAME_FORMAT_PATTERN.match(_GetFileNameWithoutPath(filename)):
    _Error(filename, 0, 'whitespace/filename', 5,
      'Filename format error: "xxx_xxx.h"')


def GuardCheckForCopyright(filename, file_extention, lines):
  """检查文件是否有copyright.
  查找文件的第0~11行, 检查其中是否有"Copyright"字符串

  Errors: 
    category: [legal/copyright]
    confidence: [5]
    message: [File need Copyright: "Copyright [year] <Copyright Owner>"]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """

  for line_num in range(_LINE_LENS_RANGE_START, min(_LINE_LENS_TRUE, 11)):
    if re.search(r'Copyright', lines[line_num], re.I):
      break

  _Error(filename, 0, 'legal/copyright', 5,
        'No copyright message found.  '
        'Need Copyright: "Copyright [year] <Copyright Owner>"')


def GuardCheckForHashIfFormatAndReplaceWithBlankLine(filename, file_extention, lines):
  """检查宏书写规范.
  1. 每行顶格书写: '#if' 前不能有空格.
  2. 需要顶格书写的有: '#if', '#endif', '#endif'
  TODO: 3. 检查宏命名规范: 全部只包含大写英文字母[A-Z]和下划线[_]
      4. #if 后可能有注释, 是否需要在注释检查完后检查此项
      5. 有些'{}'在#if里面，导致'{'和'}'数量不等

  Errors: 
    category: [whitespace/hash_if_format]
    confidence: [5]
    message: [Marco format error: no indentation before "#" symbol."]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  # rule = re.compile(r'\s+(#if|#endif|#elif|#define)')
  # for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
  #   if rule.match(lines[line_num]):
  #     _Error(filename, line_num, 'whitespace/marco', 5,
  #       'Marco format error: no indentation before "#" symbol.')
  _HASH_IF_PATTERN = re.compile(r'\s*#if|#elif|#else|#endif')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    line = lines[line_num]
    start_line = 0
    stop_line = 0
    if _HASH_IF_PATTERN.search(line):
      start_line = line_num
      if _CountLeftSpace(line) > 0:
        _Error(filename, line_num, 'whitespace/hash_if_format', 5,
        'Marco format error: no indentation before "#" symbol.')
      stop_line = _FindMultiLineWithBackslash(lines, start_line, _LINE_LENS_RANGE_STOP)
      _SaveConstructInfoAndReplaceLineWithBlank(_HASH_IF_CONSTRUCT_INFO, lines, start_line, start_line, stop_line, 1)


def GuardCheckForHashDefineFormatAndReplaceWithBlank(filename, file_extention, lines):
  """检查'#define'书写规范, 并将其替换为空行
  1. 每行顶格书写
  2. 多行模式最后一行不应有\
  3. #define与宏之间, 只能有一个空格
  4. #define后的宏, 应全部大写
  TODO: 3. 检查规范
        4. #define 后可能有注释, 是否需要在注释检查完后检查此项

  Errors: 
    category: [whitespace/hash_define_format]
    confidence: [5]
    message: []

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  _HASH_DEFINE_PATTERN = re.compile(r'\s*#define')
  # for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
  line_num = _LINE_LENS_RANGE_START
  while line_num <= _LINE_LENS_RANGE_STOP:
    line = lines[line_num]
    start_line = 0
    stop_line = 0
    if _HASH_DEFINE_PATTERN.search(line):
      start_line = line_num
      stop_line = _FindMultiLineWithBackslash(lines, start_line, _LINE_LENS_RANGE_STOP)
      _SaveConstructInfoAndReplaceLineWithBlank(_HASH_DEFINE_CONSTRUCT_INFO, lines, start_line, start_line, stop_line, 1)
      # if line.strip()[-1] != '\\': # 单行模式
      #   stop_line = start_line
      #   _SaveConstructInfoAndReplaceLineWithBlank(_HASH_DEFINE_CONSTRUCT_INFO, lines, start_line, start_line, stop_line, 1)
      #   line_num += 1
      #   continue
      # else: #多行模式
      #   for i in range(line_num, _LINE_LENS_RANGE_STOP):
      #     if lines[i].strip(): # 此行不为空
      #       if lines[i].strip()[-1] != '\\': # 找到结束行
      #         stop_line = i
      #         _SaveConstructInfoAndReplaceLineWithBlank(_HASH_DEFINE_CONSTRUCT_INFO, lines, start_line, start_line, stop_line, 1)
      #         line_num = i + 1
      #         break
      #     else: # 此行为空, 说明格式有问题, 但确实已经结束
      #       stop_line = i - 1
      #       _SaveConstructInfoAndReplaceLineWithBlank(_HASH_DEFINE_CONSTRUCT_INFO, lines, start_line, start_line, stop_line, 1)
      #       line_num = i
      #       break
      #   continue
    line_num += 1


def GuardCheckForNewlineAtEndOfFile(filename, file_extention, lines):
  """检查文件最后一行是否为空行.
  1. 文件最后一行, 应为空行.
    Errors: 
      category: [whitespace/eof_newline]
      confidence: [5]
      message: [Need new-line at the end of file]
  2. 文件倒数第二行, 应有代码. 
    Errors: 
      category: [whitespace/eof_newline]
      confidence: [5]
      message: [Only 1 blank line at the end of the file.]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  if lines[-2].strip():
    _Error(filename, _LINE_LENS_TRUE, 'whitespace/eof_newline', 5,
      'Need new-line at the end of file')
  elif not lines[-3].strip():
    _Error(filename, _LINE_LENS_TRUE, 'whitespace/eof_newline', 5,
      'Only 1 blank line at the end of the file.')


def GuardCheckForLineLength(filename, file_extention, lines):
  """检查每行字符长度
  1. 包含前后空白字符.
  2. 最大字符数量由"_MAX_LINE_LENS"定义.

  Errors: 
    category: [whitespace/line_length]
    confidence: [5]
    message: [Lines should be <= %i characters long, but you have %i]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if len(lines[line_num]) > _MAX_LINE_LENS:
      _Error(filename, line_num, 'whitespace/line_length', 5,
        'Lines should be <= %i characters long, but you have %i' % (_MAX_LINE_LENS, len(lines[line_num])))


def GuardCheckForLineEndingWithBlank(filename, file_extention, lines):
  """检查行尾无用空白字符
  1. 检查行尾是否有空白(空格, TAB, 软换行等)
  TODO: 2. 将行尾的空白字符去掉drop, 便于后续规范检查, 减少重复报错.

  Errors: 
    category: [whitespace/unused_space]
    confidence: [5]
    message: [Please remove unused space or tab at end of the line]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  _LINE_END_WITH_SPACE_PATTERN = re.compile(r'^.*\s+$')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _LINE_END_WITH_SPACE_PATTERN.match(lines[line_num]):
      _Error(filename, line_num, 'whitespace/unused_space', 5,
        'Please remove unused space or tab at end of the line')


def GuardCheckForTabInLine(filename, file_extention, lines):
  """检查否有非法符号
  1. 文件中不能包含'\\t', '\\f', '\\r'等字符.

  Errors: 
    category: [whitespace/illegal_tab]
    confidence: [5]
    message: [Please remove illegal symbols: "\\t", "\\f", "\\r".]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  _TAB_symbol_IN_LINE_PATTERN = re.compile(r'\r|\t|\f')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _TAB_symbol_IN_LINE_PATTERN.search(lines[line_num]):
      _Error(filename, line_num, 'whitespace/illegal_tab', 5,
        'Please remove illegal symbols: "\\t", "\\f", "\\r".')


def GuardCheckForLanguage(filename, file_extention, lines):
  """检查是否有中文字符
  1. 文件中不能包含中文字符.

  Errors: 
    category: [whitespace/language]
    confidence: [5]
    message: [Cannot contain Chinese characters.]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  _LANGUAGE_CHINESE_PATTERN = re.compile(r'[\u4e00-\u9fa5]')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
      if _LANGUAGE_CHINESE_PATTERN.search(lines[line_num]):
        _Error(filename, line_num, 'whitespace/language', 5,
          'Cannot contain Chinese characters.')


def GuardCheckForIncludeFormat(filename, file_extention, lines):
  """检查'#include'格式.
  1. '#include'顶行写, 前面不能有空格.
  2. '#include'后只有一个空格, 不能有多个空格
  3. 需要使用双引号, 不使用尖括号: 正确: '#include "xxx.h"',  错误: '#include <xxx.h>'
  4. 不能带路径.错误: '#include "../aaa/bbb/xxx.h"'
  TODO: 同一个客户的头文件放在一起.比如Green客户的头文件, 都带'green_'前缀, 需要放到一起.

  Errors: 
    category: [whitespace/include]
    confidence: [5]
    message: ["#include" format error.]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  _INCLUDE_PATTERN = re.compile(r'^\s*#include')
  _INCLUDE_FORMAT_PATTERN = re.compile(r'^#include "[^..].*\.h"$')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _INCLUDE_PATTERN.match(lines[line_num]):
      if not _INCLUDE_FORMAT_PATTERN.match(lines[line_num]):
        _Error(filename, line_num, 'whitespace/include', 5,
          '"#include" format error.')
      _SaveConstructInfoAndReplaceLineWithBlank(_INCLUDE_CONSTRUCT_INFO, lines, line_num, line_num, line_num, 1)


def GuardCheckForIncludeInHeaderFile(filename, file_extention, lines):
  """检查头文件的文件引用.
  1. 头文件中, 不应该包含其他头文件(不能有'#include'符号).

  Errors: 
    category: [whitespace/header_file]
    confidence: [5]
    message: [Header file(xxx.h) cannot include headerfiles.]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  if file_extention != 'h':
    return
  _INCLUDE_PATTERN = re.compile(r'^\s*#include')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _INCLUDE_PATTERN.match(lines[line_num]):
      _Error(filename, line_num, 'whitespace/header_file', 5,
        'Header file(xxx.h) cannot include headerfiles.')


def GuardCheckForHeaderFileHaveMarcoProtect(filename, file_extention, lines):
  """检查头文件的宏保护.
  1. 使用与文件名同名的宏, 如头文件aaa_bbb.h, 应使用__AAA_BBB_H__来保护.
  2. 前后需使用双下划线"__"
  example:
    filename: aaa_bbb.h
    #ifndef __XXX_XXX_H__ // 必须在第一行.
    #define __XXX_XXX_H__ // 必须在第二行.
    ///code
    #endif // __XXX_XXX_H__ // 必须在倒数第一行.

  Errors: 
    category: [whitespace/header_file]
    confidence: [5]
    message: [Header file(xxx.h) need to be protected correctly.]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  if file_extention != 'h':
    return
  marco = "__" + re.sub(r'\.', '_', _GetFileNameWithoutPath(filename).upper()) + "__"
  if (lines[1] != '#ifndef ' + marco) and (lines[2] != '#define ' + marco) and (lines[-3] != '#endif // ' + marco):
    _Error(filename, 0, 'whitespace/header_file', 5,
        'Header file(xxx.h) need to be protected correctly.')

def GuardCheckForSrcFileUseExtern(filename, file_extention, lines):
  """禁止在'.c'文件中使用'extern'.
  1. '.c'文件中, 不能有extern开头的行.
  TODO: '.h'文件中的extern需要限制?

  Errors: 
    category: [whitespace/c_file]
    confidence: [5]
    message: [Src file(xxx.c) forbid to use "extern".]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  if file_extention != 'c':
    return

  _EXTERN_PATTERN = re.compile(r'^\s*extern')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _EXTERN_PATTERN.match(lines[line_num]):
      _Error(filename, 0, 'whitespace/c_file', 5,
        'Src file(xxx.c) forbid to use "extern".')


def GuardCheckForSingleLineCommentAndReplaceByBlank(filename, file_extention, lines):
  """检查单行注释('//')规范
  单行注释, 分为新行模式 和 代码行模式: 
  1. 新行模式: 整个注释位于一个独立的新行, 前面没有代码.
  2. 代码行模式: 注释位于代码最后.

  3. 公共规则: 
    a. "//"符号与注释间, 应有且只有一个空格: "// comments"
    b. 禁止注释内容为空的无效注释: 
      "//////  "
      "//   "
    c. 不能同时有多于两个的斜杠:'///'
  4. 新行模式规则: 
    a. 当"//"位于新行时, 应位于代码上方一行, 无空行:
      "// comments"
      "code;"
    b. 当"//"位于新行时, 缩进应保持与代码一致(在缩进中检查)
      "    // comments"
      "    code;"
  5. 代码行模式规则: 
    a. 当"//"位于代码行尾时,应与代码间至少有1个空格: "code; // comments"
  
  Note: 在规范检查过程中, 会将这些注释替换为空, 不影响行数, 也不修改原文件.

  Errors: 单行模式: '//'和注释间, 没有空格, 或有多个空格.
    category: [whitespace/sigle_line_comment]
    confidence: [5]
    message: [(Newline mode) Format error, syntax: "// xxx".]

  Errors: 单行模式: 注释不应该在文件的最后一行.
    category: [whitespace/sigle_line_comment]
    confidence: [5]
    message: [(Newline mode) Should not be the last line.]

  Errors: 单行模式: 注释的下一行不应该为空.
    category: [whitespace/sigle_line_comment]
    confidence: [5]
    message: [(Newline mode) Should not be blank in next line.]

  Errors: 单行模式: 注释的缩进, 应和下一行相等.
    category: [whitespace/sigle_line_comment]
    confidence: [5]
    message: [(Newline mode) Indentation error, should be same with next line.]

  Errors: 多行模式: '//'前和后, 有且只有一个空格.
    category: [whitespace/sigle_line_comment]
    confidence: [5]
    message: [(Code-line mode) format error. syntax: "code // comments".]

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """

  _SINGLE_LINE_COMMENTS_NEWLINE_MODE_PATTERN = re.compile(r'^\s*///*')
  _SINGLE_LINE_COMMENTS_NEWLINE_MODE_RULE_PATTERN = re.compile(r'^\s*// \S.*$')
  _SINGLE_LINE_COMMENT_CODE_LINE_MODE_PATTERN = re.compile(r'^\s*[^ \t\r\n\f/].*///*')
  _SINGLE_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN = re.compile(r'\S // \S.*$')

  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _SINGLE_LINE_COMMENTS_NEWLINE_MODE_PATTERN.match(lines[line_num]): # 检查是否单行注释的新行模式
      if not _SINGLE_LINE_COMMENTS_NEWLINE_MODE_RULE_PATTERN.match(lines[line_num]): # 检查注释规范是否正确.
        _Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Newline mode) Format error, syntax: "// xxx".')
      if line_num == _LINE_LENS_TRUE: # 注释不应该为最后一行
        _Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Newline mode) Should not be the last line.')
      if not lines[line_num + 1].strip(): # 注释的下一行, 应该有代码
        _Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Newline mode) Should not be blank in next line.')
      if _CountLeftSpace(lines[line_num]) != _CountLeftSpace(lines[line_num + 1]): # 检查缩进是否与下一行相等
        # 下一行是否为"}", 若是, 则应有4个空格的缩进
        if not (re.match(r'^\s*}.*$', lines[line_num + 1]) and (_CountLeftSpace(lines[line_num]) == _CountLeftSpace(lines[line_num + 1]) + 4)):
          _Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
            '(Newline mode) Indentation error, should be same with next line.')
      # replace this lines as blank
      lines[line_num] = ''

    if _SINGLE_LINE_COMMENT_CODE_LINE_MODE_PATTERN.match(lines[line_num]):
      if not _SINGLE_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN.search(lines[line_num]):
        _Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Code-line mode) format error. syntax: "code // comments"')
      # replace this comments as blank
      # print(line_num, lines[line_num])
      lines[line_num] = re.sub(r'//.*$', '', lines[line_num])
      # print(line_num, lines[line_num])


def GuardCheckForMultiLineCommentAndReplaceByBlank(filename, file_extention, lines):
  """检查多行注释规范"/* */":
  多行注释, 分为 '单行模式', '代码单行模式', '多行模式'.
  1. 单行模式: 整个注释位于新行, 前后无代码. 
  2. 代码单行模式: 注释位于代码行行尾或中间. 
  3. 多行模式: 整个注释包含2行或以上. 
  example:
  /**
   * comments
   * comments
   *
   */

  1. 单行模式规则: 
    a. 注释内容, 与注释符号之间, 应有且仅有一个空格:/* xxx */
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Single-line mode) Format error syntax: "   /* xxx */".]
    b. 注释不应为最后一行. 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Single-line mode) Should not be the last line.']
    c. 当下一行不为空, 且下一行有'}'符号时,  缩进应比下一行多4个空格
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Single-line mode) Indentation error, next line is '}', 4 more space in indentation.]
    d. 当下一行不为空, 且下一行没有'}'符号时, 缩进应与下一行一致。
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Single-line mode) Indentation error, indentation should be same with next line.]
    e. 当下一行为空, 且上一行不为空时, 缩进应与上一行一致。
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Single-line mode) Indentation error, indentation should be same with previous line.]
    f. 当下一行为空, 且上一行为空时, 此注释不应该存在, 应该删除。
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Single-line mode) Indentation error, should not exist independently, please delete it.]

  2. 单行代码模式规则: 
    a. 注释内容与注释符之间, 有且只有一个空格: "/* comments */"
    b. 注释应位于行末, 不应夹在代码之间: "code /* comments */ code;"
    c. 注释内容与代码之间, 有且只有一个空格: "code; /* comments */"
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Code-line mode) Syntax error: "code /* comments */"]
  3. 多行模式规则: 
    a. 起始行应使用新行, 前后不能有代码, 起始行使用符号"/**". 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Multi-line mode) Start-line syntax error: "newline /**$".]
    b. 起始行的缩进, 应比下一行少一个空格: " *" 比 "/**"多一个空格. 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Multi-line mode) Start-line indentation error.]
    c. 中间行使用符号" * comments" 或 " *", 注意'*'前的空格. 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Multi-line mode) Mid-line syntax error: " * comments$".]
    d. 中间行缩进, 应和下一行一致. 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Multi-line mode) Mid-line indentation error.]
    e. 结束行应使用新行, 前后不能有代码, 使用符号" */",  注意空格. 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Multi-line mode) Stop-line syntax error: " */$'.]
    f. 结束行, 当下一行不为空时, 缩进应比下一行多一个空格: " */"前面有一个空格. 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Multi-line mode) Stop-line indentation error. Next line is not None.]
    g. 结束行, 当下一行为空时, 缩进应为1个空格: " */"前面有一个空格. 
      Errors:
      category: [whitespace/multi_line_comment]
      confidence: [5]
      message: [(Multi-line mode) Stop-line indentation error. Next line is None.]

  Note: 在规范检查过程中, 会将这些注释替换为空, 不影响行数, 也不修改原文件.

  Args:
    filename: 需要处理的文件名.
    file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
    lines: filename中的所有行列表[].
  """
  _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_PATTERN = re.compile(r'^\s*/\*.*\*/\s*$') # "newline /* comments */"
  _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_RULE_PATTERN = re.compile(r'^\s*/\* \S+ \*/$')
  _MULTI_LINE_COMMENT_CODE_LINE_MODE_PATTERN = re.compile(r'\S.*/\*.*\*/') # "code /* comments */"
  _MULTI_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN = re.compile(r'\S /\* \S.*\S \*/$')
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_PATTERN = re.compile(r'/\*+') # "/**"
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_RULE_PATTERN = re.compile(r'^\s*/\**$')
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_MID_RULE_PATTERN = re.compile(r'^\s* \*(.*\S)?$')
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_PATTERN = re.compile(r'\*/') # " */"
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_RULE_PATTERN = re.compile(r'^\s* \*/$')

  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_PATTERN.match(lines[line_num]): # 是否为单行模式
      if not _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_RULE_PATTERN.match(lines[line_num]): # 单行模式是否规范
        # print(line_num, lines[line_num])
        _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Single-line mode) Format error syntax: "   /* xxx */".')
      if line_num == _LINE_LENS_TRUE: # 注释不应该为最后一行
        _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Single-line mode) Should not be the last line.')
      # if not lines[line_num + 1].strip(): # 注释的下一行, 应该有代码 # 对于需要start 和 end的, 此处无法检查, 取消.
      #   _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
      #     '(Single-line mode) Should not be blank in next line.')
      # if (_CountLeftSpace(lines[line_num]) != _CountLeftSpace(lines[line_num + 1])) and (not lines[line_num + 1].strip()): # 检查缩进是否与下一行相等 # 当下一行部位空时, 才检查缩进
      #   # 下一行是否为"}", 若是, 则应有4个空格的缩进
      #   if not (re.match(r'^\s*}.*$', lines[line_num + 1]) and (_CountLeftSpace(lines[line_num]) == _CountLeftSpace(lines[line_num + 1]) + 4)):
      #     _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
      #       '(Single-line mode) Indentation error, should be same with next line.')
      
      # 开始检查缩进
      if lines[line_num + 1].strip(): # 当下一行不为空时
        if re.match(r'^\s*}.*$', lines[line_num + 1]): # 如果下一行有'}', 则需要增加4个空格的缩进
          if (_CountLeftSpace(lines[line_num])) != (_CountLeftSpace(lines[line_num + 1]) + 4):
            _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Single-line mode) Indentation error, next line is "}", 4 more space in indentation.')
        else: # 下一行没有'}'时, 缩进应与下一行一致
          if _CountLeftSpace(lines[line_num]) != _CountLeftSpace(lines[line_num + 1]):
            _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Single-line mode) Indentation error, indentation should be same with next line.')
      else: # 当下一行为空时
        if lines[line_num - 1].strip(): # 如果上一行不为空, 缩进应与上一行一致。
          if _CountLeftSpace(lines[line_num]) != _CountLeftSpace(lines[line_num - 1]):
            _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Single-line mode) Indentation error, indentation should be same with previous line.')
        else: # 如果上一行也为空, 应删除此注释
          _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Single-line mode) Indentation error, should not exist independently.')

      lines[line_num] = ''

  # 把单行模式全部替代空格后, 重新开始匹配, 避免注释影响.
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _MULTI_LINE_COMMENT_CODE_LINE_MODE_PATTERN.search(lines[line_num]): # 代码行的单行模式,  缩进随代码行本身, 不检查此行缩进
      if not _MULTI_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN.search(lines[line_num]): # 注释是否规范
        _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Code-line mode) Syntax error: "code /* comments */"')
      lines[line_num] = re.sub(r'/\*.*?\*/', '', lines[line_num])
  
  start_line = None # 记录是否已找到一个多行注释的起始行.
  # 把单行模式, 代码行模式, 全部代空后, 只剩下多行模式的注释, 包括新行的多行模式, 和跨代码行的多行模式.
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP): 
    if _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_PATTERN.search(lines[line_num]): # 检查是否为多行模式的起始行
      if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_RULE_PATTERN.match(lines[line_num]): # 检查规范
        _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Start-line syntax error: "newline /**$".')
      start_line = line_num # 标记已找到注释起始行, 后面开始匹配中间行和结束行

      # 检查多行模式开始行的缩进, 从上到下, 本行与下一行比较
      if _CountLeftSpace(lines[line_num]) + 1 != _CountLeftSpace(lines[line_num + 1]):
        _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Start-line indentation error.')

       # 将注释替换成空白, 注意"/*"前面可能有代码.
      lines[line_num] = re.sub(r'/\*.*$', '', lines[line_num])

      continue # 寻找下一行, 不能在开始行匹配中间行和结束行规则

    # 已经找到了注释开始行, 开始匹配中间行和结束行
    if start_line:
      if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_PATTERN.search(lines[line_num]): # 中间行(不是结束行)
        if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_MID_RULE_PATTERN.match(lines[line_num]): # 检查中间行规则
          _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Mid-line syntax error: " * comments$".')
  
        # 检查多行模式的中间行缩进, 从上到下, 本行与下一行比较
          if _CountLeftSpace(lines[line_num]) != _CountLeftSpace(lines[line_num + 1]):
            _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Multi-line mode) Mid-line indentation error.')

        lines[line_num] = '' # 直接将中间行替换为空白
      else: # 结束行
        if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_RULE_PATTERN.match(lines[line_num]): # 检查结束行规则
          _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Stop-line syntax error: " */$.')

        """
        检查多行模式的结束行缩进, 从上到下, 本行与下一行比较
        若下一行不为空(有代码), 则此多行注释应匹配代码行缩进
  
        当下一行为空, 则此多行注释应该为顶行注释(所以如果此多行注释在{}, 则注释末行的下一行, 一定不能为空)
          当下一行为空时.由于之前已经将"//"和单行模式的"/**/"都替换为空了, 所以此处下一行也有可能是被置换为空的那行(原先为单行注释).
          所以多行模式, 紧接下一行, 如果为单行注释, 这种情况是不允许的.
        """
        if not lines[line_num + 1].strip(): # 下一行不为空
          if _CountLeftSpace(lines[line_num]) != _CountLeftSpace(lines[line_num + 1]) + 1:
            _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Multi-line mode) Stop-line indentation error. Next line is not None.')
        else:
          if _CountLeftSpace(lines[line_num]) != 1:
            _Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Multi-line mode) Stop-line indentation error. Next line is None.')
            
        start_line = None # 注释已经结束, 重置起始标志, 便于下一个注释的查找.

        # 将注释替换为空白, 注意'*/'后面可能还有代码, 使用非贪婪模式
        lines[line_num] = re.sub(r'.*?\*/', '', lines[line_num])

      # print(line_num, lines[line_num])
  # print(lines)


def _GuardCheckForInvalidIncrement(filename, file_extention, lines):
  """Checks for invalid increment *count++.

  For example following function:
  void increment_counter(int* count) {
    *count++;
  }
  is invalid, because it effectively does count++, moving pointer, and should
  be replaced with ++*count, (*count)++ or *count += 1.

  Args:
    filename: The name of the current file.
    clean_lines: A CleansedLines instance containing the file.
    linenum: The number of the line to check.
    error: The function to call with any errors found.
  """
# Matches invalid increment: *count++, which moves pointer instead of
# incrementing a value.
  _RE_PATTERN_INVALID_INCREMENT = re.compile(r'^\s*\*\w+(\+\+|--);')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _RE_PATTERN_INVALID_INCREMENT.match(lines[line_num]):
      _Error(filename, line_num, 'runtime/invalid_increment', 5,
            'Changing pointer instead of value (or unused value of operator*).')


def _FindoutStringAndReplaceWithSpecialSymbol(filename, file_extention, lines):
  """查找字符串, 并使用一个固定的symbol替代, 消除对后续检查的影响.
  1. 单引号字符串
  2. 多引号字符串
  3. 替换为'STRING'
  4. 多行字符串, 每行都应使用"", 不使用"加换行符的形式. 
  TODO: 1. 限制Log中字符串长度
        2. Log字符串中, 不应有__func__, __line__等宏
        3. 字符串中的,;等字符, 是否需要检查规则
  """
  _CHAR_PATTERN = re.compile(r'\'.*\'')
  _SINGLE_LINE_STRING_PATTERN = re.compile(r'\".*\"')
  _MULTI_LINE_STRING_PATTERN = re.compile(r'\"|\'')
  _MULTI_LINE_STRING_START_REPLACE_PATTERN = re.compile(r'\".*\\$')
  _MULTI_LINE_STRING_MID_REPLACE_PATTERN = re.compile(r'\S.*$')
  _MULTI_LINE_STRING_STOP_REPLACE_PATTERN = re.compile(r'\S.*\"')
  _EXTERN_C_PATTERN = re.compile(r'^\s*extern\s*"C"')
  multi_line_start = None
  multi_line_stop = None
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    line = lines[line_num]
    if _EXTERN_C_PATTERN.match(line):
      continue
    if _CHAR_PATTERN.search(lines[line_num]): # 首先替换掉单引号的char类型, 此类型不能跨行, 但是包含所有的字符符号（包括", +等）
      lines[line_num] = re.sub(_CHAR_PATTERN, '_CHAR', lines[line_num])
    if _SINGLE_LINE_STRING_PATTERN.search(lines[line_num]): # 然后替换掉单行的"字符串"
      lines[line_num] = re.sub(_SINGLE_LINE_STRING_PATTERN, '_STRING', lines[line_num])
    if _MULTI_LINE_STRING_PATTERN.search(lines[line_num]): # 经过上面两次替换后, 如果还有引号,说明有跨行的字符串, 但是跨行字符串应符合每行都有两个双引号. 
      if not multi_line_start: # 多行字符串第一行
        multi_line_start = line_num
        # print('1', filename, line_num, line)
        lines[line_num] = re.sub(_MULTI_LINE_STRING_START_REPLACE_PATTERN, '_STRING', lines[line_num])
        # print('1', filename, line_num, lines[line_num])
        continue
      else: # 多行字符串最后一行
        multi_line_stop = line_num
        # print('3', filename, line_num, line)
        lines[line_num] = re.sub(_MULTI_LINE_STRING_STOP_REPLACE_PATTERN, '_STRING', lines[line_num])
        # print('3', filename, line_num, lines[line_num])
        multi_line_start = None
        multi_line_stop = None
    if multi_line_start and not multi_line_stop: # 多行字符串中间行
      # print('2', filename, line_num, line)
      lines[line_num] = re.sub(_MULTI_LINE_STRING_MID_REPLACE_PATTERN, '_STRING', lines[line_num])
      # print('2', filename, line_num, lines[line_num])
    # elif multi_line_start and multi_line_stop:
    #   multi_line_start = None
    #   multi_line_stop = None


def _FindoutAllMultiLineBLock(filename, file_extention, lines):
  """找出所有的多行主体"{}"结构, 包括fuction, enum, struct, union.
  只寻找最外层结构, 如果内部有嵌套, 嵌套内容属于最外一层的结构. 
  example: 此处寻找到的是enum1这个整体结构, 内部包含了enum2和enum3
    enum1 {
      ...
      enum2 {
        ...
        enum3 {
          ...
        }
      }
      ...
    }
  """
  
  _ENUM_PATTERN = re.compile(r'^\s*(typedef\b)?\s*enum\b\s*')
  _STRUCT_PATTERN = re.compile(r'^\s*(typedef\b)?\s*struct\b\s*')
  _UNION_PATTERN = re.compile(r'^\s*(typedef\b)?\s*union\b\s*')
  # _FUNCTION_PATTERN = re.compile(r'\w+(\s|\n|\*)+\w+(\s|\n)*\((\s|\n|!|%|&|\(|\)|\*|\+|,|-|\/|\w|\[|\\|\]|\^|\||~|<{2}|>{2})*?\)(\n|\s)*')
  _FUNCTION_PATTERN = re.compile(r'\w+(\s|\n|\*)+\w+(\s|\n)*\((\s|\n|!|%|&|\(|\)|\*|\+|,|-|\/|\w|\[|\\|\]|\^|\||~|<{2}|>{2})*?(\n|\s*)')
  _VARIABLE_DEFINE_PATTERN = re.compile(r'\s*[a-zA-Z0-9_]+(\[.*\])*\s*=\s*{*')
  # _VARIABLE_DEFINE_PATTERN2 = re.compile(r'\s*[a-zA-Z0-9_]+(\[.*\])*\s*=\s*{*')
  #extern "C" 处理
  # for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
  line_num = _LINE_LENS_RANGE_START
  while line_num <= _LINE_LENS_RANGE_STOP:
    line = lines[line_num]
    checked_line_count = 1
    if line.strip():
      if _FUNCTION_PATTERN.search(line):
        checked_line_count = _FindOneBracketsConstruct(_FUNCTION_CONSTRUCT_INFO, lines, line_num, _FUNCTION_CONSTRUCT_INFO_LABEL)
      elif _ENUM_PATTERN.match(line):
        checked_line_count = _FindOneBracketsConstruct(_ENUM_CONSTRUCT_INFO, lines, line_num, _ENUM_CONSTRUCT_INFO_LABEL)
      elif _STRUCT_PATTERN.match(line):
        checked_line_count = _FindOneBracketsConstruct(_STRUCT_CONSTRUCT_INFO, lines, line_num, _STRUCT_CONSTRUCT_INFO_LABEL)
      elif _UNION_PATTERN.match(line):
        checked_line_count = _FindOneBracketsConstruct(_UNION_CONSTRUCT_INFO, lines, line_num, _UNION_CONSTRUCT_INFO_LABEL)
      elif _VARIABLE_DEFINE_PATTERN.search(line):
        checked_line_count = _FindOneBracketsConstruct(_VARIABLE_DEFINE_INFO, lines, line_num, _VARIABLE_DEFINE_INFO_LABEL)
    line_num = line_num + checked_line_count
  # print('test')


def _CheckForCommonRules(filename, raw_line, line_num):
  _SPACE_RULE_PATTERN = re.compile(r'\s\s+') # 任意两个符号之间，空白字符不能超过1个
  _COMMA_RULE_PATTERN = re.compile(r'\s+,') # 当逗号前面没有空格
  _COMMA_RULE_PATTERN2 = re.compile(r',(\S|\s\s+\S)') # 当逗号后面没有空格，或者有两个以上空格时
  _SEMICOLON_RULE_PATTERN = re.compile(r'\s+;') # 当分号前有一个或多个空格
  _BRACKETS_RULE_PATTERN = re.compile(r'\(\s|\s\)|\[\s|\s\]|\{\s|\s\}') # 括号与内部字符之间，不应有空格
  striped_line = raw_line.strip()
  if _SPACE_RULE_PATTERN.search(striped_line): # 任意两个符号之间，空白字符不能超过1个
    # print('c1', filename, line_num, raw_line)
    _Error(filename, line_num, 'whitespace/redundant_space', 5,
          'No more than 2 space between 2 symbols.')
  elif _COMMA_RULE_PATTERN.search(striped_line) or _COMMA_RULE_PATTERN2.search(striped_line):
    # print('c2', filename, line_num, raw_line)
    _Error(filename, line_num, 'whitespace/comma_rule', 5,
          'No space in front of "," and only 1 space after ",".')
  elif _SEMICOLON_RULE_PATTERN.search(striped_line):
    # print('c3', filename, line_num, raw_line)
    _Error(filename, line_num, 'whitespace/semicolon_rule', 5,
          'No space in front of ";".')
  elif _BRACKETS_RULE_PATTERN.search(striped_line):
    # print('c4', filename, line_num, raw_line)
    _Error(filename, line_num, 'whitespace/brackets_rule', 5,
          'In brackets, no space after "(" "[" "{", no space in front of ")" "]" "}"')


def GuardCheckForRestOfTheLines(filename, file_extention, lines):
  """
  1. '='
  2. ','
  3. ';'
  4. 中间空格
  5. '[]'
  6. '()'
  6. 全局变量命名,
  7. 魔数

  examples:
    'typedef void (*UX_BTEVENT_CALLBACK)(uint8_t eventId, void *buff, uint16_t len);'
    'static void UX_BtSwitchActiveDevIfNeed(void);'
    'static STD_COMMU_PKG_HEAD_T* g_pStdPkgHead;', 'static CMD_SET_T g_cmdSet[MAX_CMDSET_COUNT];'
    'INIT_CALL_APP(UX_ManagerInit);'
    'static CMD_SET_T g_cmdSet[MAX_CMDSET_COUNT];'
    'static int32_t g_stageCounter = 0;'
    'static int32_t g_stageCounter = 0;'
    'enum weekday a,b,c;'
    'static int32_t g_stageCounter = 0;'
    extern "C" {
    'typedef void (*init_fn)(void);'
  """
  _SPACE_RULE_PATTERN = re.compile(r'\s\s+') # 任意两个符号之间，空白字符不能超过1个
  _COMMA_RULE_PATTERN = re.compile(r'\s+,') # 当逗号前面没有空格
  _COMMA_RULE_PATTERN2 = re.compile(r',(\S|\s\s+\S)') # 当逗号后面没有空格，或者有两个以上空格时
  _SEMICOLON_RULE_PATTERN = re.compile(r'\s+;') # 当分号前有一个或多个空格
  _BRACKETS_RULE_PATTERN = re.compile(r'\(\s|\s\)|\[\s|\s\]|\{\s|\s\}') # 括号与内部字符之间，不应有空格
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    line = lines[line_num]
    striped_line = line.strip()
    if _SPACE_RULE_PATTERN.search(striped_line): # 任意两个符号之间，空白字符不能超过1个
      # print('c1', filename, line_num, line)
      _Error(filename, line_num, 'whitespace/redundant_space', 5,
            'No more than 2 space between 2 symbols.')
    elif _COMMA_RULE_PATTERN.search(striped_line) or _COMMA_RULE_PATTERN2.search(striped_line):
      # print('c2', filename, line_num, line)
      _Error(filename, line_num, 'whitespace/comma_rule', 5,
            'No space in front of "," and only 1 space after ",".')
    elif _SEMICOLON_RULE_PATTERN.search(striped_line):
      # print('c3', filename, line_num, line)
      _Error(filename, line_num, 'whitespace/semicolon_rule', 5,
            'No space in front of ";".')
    elif _BRACKETS_RULE_PATTERN.search(striped_line):
      # print('c4', filename, line_num, line)
      _Error(filename, line_num, 'whitespace/brackets_rule', 5,
            'In brackets, no space after "(" "[" "{", no space in front of ")" "]" "}"')
    elif _CountLeftSpace(line) and line.strip():
      # print('c5', filename, line_num, line)
      _Error(filename, line_num, 'whitespace/redundant_indentation', 5,
            'No indentation in front of this line.')

    # 检查全局变量驼峰格式


# def _check_indentation_format
def GuardCheckForStructRule(filename, file_extention, lines):
  _INDENT_SPACE_COUNT = 4
  _COMMA_RULE_PATTERN = re.compile(r'\s+,') # 当逗号前面没有空格
  _COMMA_RULE_PATTERN2 = re.compile(r',(\S|\s\s+\S)') # 当逗号后面没有空格，或者有两个以上空格时
  _SEMICOLON_RULE_PATTERN = re.compile(r'\s+;') # 当分号前有一个或多个空格
  _PARENTHESIS_SQUAREBRACKETS_RULE_PATTERN = re.compile(r'\(\s|\s\)|\[\s|\s\]') # 圆括号和方括号,与内部字符之间，不应有空格
  _LEFT_BRACE_RULE_PATTERN = re.compile(r'\S\{|\S\s\s+\{|\{\s*\S')
  _RIGHT_BRACE_RULE_PATTERN = re.compile(r'\S\s*\}|\}(\w|\s\s+)')
  _STRUCT_NAME_PATTERN1 = re.compile(r'(?<=struct).+(?={)') # 第一行如果有定义结构体名称
  _STRUCT_NAME_PATTERN2 = re.compile(r'(?<=\))\s*\w+(\s*,\s*\w+\s*)*\s*(?=;)') # 如果最后一行有圆括号
  _STRUCT_NAME_PATTERN3 = re.compile(r'(?<=\})\s*\w+(\s*,\s*\w+\s*)*\s*(?=;)') # 最后一行没有圆括号
  for item in _STRUCT_CONSTRUCT_INFO:
    if item:
      current_indent_count = 0
      error_count = 0
      first_line = item['data'][0]
      last_line = item['data'][-1]
      first_line_num = item['line_start']
      last_line_num = item['line_stop']
      for line_num in range(0, len(item['data'])):
        raw_line_num = line_num + item['line_start']
        line = item['data'][line_num]
        striped_line = line.strip()

        # 当本行最后为'}'时，缩进减1
        if re.findall(r'{', line) < re.findall(r'}', line):
          current_indent_count -= _INDENT_SPACE_COUNT

        if (_CountLeftSpace(line) != current_indent_count) and (line.strip()):
          _Error(filename, raw_line_num, 'struct/indentation', 5,
            'The indentation for this line should be %i space, but you have %i' % (current_indent_count, _CountLeftSpace(line)))        
          error_count += 1

        # 检查花括规范
        if (line.find('{') != -1) and (_LEFT_BRACE_RULE_PATTERN.search(line) or (striped_line == '{')):
          _Error(filename, raw_line_num, 'struct/brace', 5,
                'Format error, should be "xxx {".')
          error_count += 1
        elif (line.find('}') != -1) and (_RIGHT_BRACE_RULE_PATTERN.search(line) or (striped_line == '}')):
          _Error(filename, raw_line_num, 'struct/brace', 5,
                'Format error, should be "};" or "} XXX, YYY;".')
          error_count += 1

        if _COMMA_RULE_PATTERN.search(striped_line) or _COMMA_RULE_PATTERN2.search(striped_line):
          # print('c2', filename, line_num, line)
          _Error(filename, raw_line_num, 'struct/comma', 5,
                'No space in front of "," and only 1 space after ",".')
          error_count += 1
        elif _SEMICOLON_RULE_PATTERN.search(striped_line):
          # print('c3', filename, line_num, line)
          _Error(filename, raw_line_num, 'struct/semicolon', 5,
                'No space in front of ";".')
          error_count += 1
        elif _PARENTHESIS_SQUAREBRACKETS_RULE_PATTERN.search(striped_line):
          # print('c4', filename, line_num, line)
          _Error(filename, raw_line_num, 'struct/brackets', 5,
                'In brackets, no space after "(" "[", no space in front of ")" "]"')
          error_count += 1          

        # 检查结构体成员对齐规则
        # 检查结构体成员驼峰格式
        # 第一排和最后一排无缩进
        # struct 命名之间只有一个空格
        
        # 当本行最后为'{'时，下一行缩进加1. 由于是逐行检查，且第一行缩进永远为0，所以需要将此判断放到最后
        if re.findall(r'{', line) > re.findall(r'}', line):
          current_indent_count += _INDENT_SPACE_COUNT
          
      # 检查结构体命名, 当花括号符合规则时再匹配, 否则容易匹配错误
      if error_count == 0:
        # struct_name_list = []
        name = _STRUCT_NAME_PATTERN1.search(first_line)
        if name:
          name = name.group().split(',')
          for i in name:
            if i.strip() != '':
              # struct_name_list.append(i.strip())
              if (not i.isupper()) or (i[-2:] != '_T'):
                _Error(filename, first_line_num, 'struct/name', 5,
                  'Struct name format error.')

        if last_line.find(')') != -1:
          name = _STRUCT_NAME_PATTERN2.search(last_line)
        else:
          name = _STRUCT_NAME_PATTERN3.search(last_line)
        if name:
          name = name.group().split(',')
          for i in name:
            if i.strip() != '':
              # struct_name_list.append(i.strip())
              if (not i.isupper()) or (i[-2:] != '_T'):
                _Error(filename, last_line_num, 'struct/name', 5,
                  'Struct name format error.')

        # for name in struct_name_list:
        #   if (not name.isupper()) or (name[-2:] != '_T'):
        #     _Error(filename, raw_line_num, 'struct/name', 5,
        #       'Struct name format error.')
        #     break


def GuardCheckForEnumRule(filename, file_extention, lines):
  _INDENT_SPACE_COUNT = 4
  _COMMA_RULE_PATTERN = re.compile(r'\s+,') # 逗号前面没有空格, 整个enum都检查
  _COMMA_RULE_PATTERN2 = re.compile(r',(\S|\s\s+)') # 逗号后面没有空格或有一个以上的空格, 只检查含有'{'或'}'的行
  _LEFT_BRACE_RULE_PATTERN = re.compile(r'\S\{|\S\s\s+\{|\{\s*\S')
  _RIGHT_BRACE_RULE_PATTERN = re.compile(r'\S\s*\}|\}(\w|\s\s+)')
  _ENUM_NAME_PATTERN1 = re.compile(r'(?<=enum).+(?={)') # 第一行如果有定义结构体名称
  _ENUM_NAME_PATTERN2 = re.compile(r'(?<=\))\s*\w+(\s*,\s*\w+\s*)*\s*(?=;)') # 如果最后一行有圆括号
  _ENUM_NAME_PATTERN3 = re.compile(r'(?<=\})\s*\w+(\s*,\s*\w+\s*)*\s*(?=;)') # 最后一行没有圆括号
  _OPERATER_RULE_PATTERN1 = re.compile(r'(\S|\s\s+)[\=\-\+]') # 运算符规则'=' '-' '+'
  _OPERATER_RULE_PATTERN2 = re.compile(r'[\=\-\+](\S|\s\s+)') # 运算符规则'=' '-' '+'
  _HEX_DIGITS_PATTERN = re.compile(r'0[xX][0-9a-fA-F]+')
  for item in _ENUM_CONSTRUCT_INFO:
    if item:
      current_indent_count = 0
      error_count = 0
      first_line = item['data'][0]
      last_line = item['data'][-1]
      first_line_num = item['line_start']
      last_line_num = item['line_stop']
      for line_num in range(0, len(item['data'])):
        raw_line_num = line_num + item['line_start']
        line = item['data'][line_num]
        striped_line = line.strip()

        # 当本行最后为'}'时，缩进减1
        if re.findall(r'{', line) < re.findall(r'}', line):
          current_indent_count -= _INDENT_SPACE_COUNT
          if _COMMA_RULE_PATTERN2.search(line): # 两个名称间的逗号, 逗号后应有一个空格
            _Error(filename, raw_line_num, 'enum/comma', 5,
              'Need 1 space after ",", and only have 1 space after it.')

        if (_CountLeftSpace(line) != current_indent_count) and (line.strip()):
          _Error(filename, raw_line_num, 'enum/indentation', 5,
            'The indentation for this line should be %i space, but you have %i' % (current_indent_count, _CountLeftSpace(line)))        
          error_count += 1

        # 检查花括规范
        if (line.find('{') != -1) and (_LEFT_BRACE_RULE_PATTERN.search(line) or (striped_line == '{')):
          _Error(filename, raw_line_num, 'enum/brace', 5,
                'Format error, should be "xxx {".')
          error_count += 1
        elif (line.find('}') != -1) and (_RIGHT_BRACE_RULE_PATTERN.search(line) or (striped_line == '}')):
          _Error(filename, raw_line_num, 'enum/brace', 5,
                'Format error, should be "};" or "} XXX, YYY;".')
          error_count += 1

        if _COMMA_RULE_PATTERN.search(striped_line):
          # print('c2', filename, line_num, line)
          _Error(filename, raw_line_num, 'enum/comma', 5,
                'No space in front of ",".')
          error_count += 1

        # 检查enum成员对齐规则
        # 检查enum成员驼峰格式
        # enum和'{'或者名称之间应只有一个空格
        # 最后一个成员, 末尾是否一定需要','

        if (line.find('{') == -1) and (line.find('}') == -1) and (line_num != 0) and (line_num != len(item['data']) - 1):
          if not _HEX_DIGITS_PATTERN.sub('_HEXDIG', line).isupper(): # 成员名应该全部大写  # '=' 号后面可能会有十六进制的0x符号，这里没必要要求大写              
            _Error(filename, raw_line_num, 'enum/member', 5,
                'All member name should be upper.')
        
        if _OPERATER_RULE_PATTERN1.search(line) or _OPERATER_RULE_PATTERN2.search(line): # 检查 '+' '-' '='之间的规则, 前后有且只有一个空格
          _Error(filename, raw_line_num, 'enum/operator', 5,
                'Only 1 space before and after the operator.')

        # 当本行最后为'{'时，下一行缩进加1. 由于是逐行检查，且第一行缩进永远为0，所以需要将此判断放到最后
        if re.findall(r'{', line) > re.findall(r'}', line):
          current_indent_count += _INDENT_SPACE_COUNT
          if _COMMA_RULE_PATTERN2.search(line): # 两个名称间的逗号, 逗号后应有一个空格
            _Error(filename, raw_line_num, 'enum/comma', 5,
              'Need 1 space after ",", and only have 1 space after it.')

        
      # 检查enum命名, 当花括号符合规则时再匹配, 否则容易匹配错误
      if error_count == 0:
        # struct_name_list = []
        name = _ENUM_NAME_PATTERN1.search(first_line)
        if name:
          name = name.group().split(',')
          for i in name:
            if i.strip() != '':
              if (not i.isupper()) or (i[-2:] != '_E'):
                _Error(filename, first_line_num, 'enum/name', 5,
                  'ENUM name format error.')
                break
              # struct_name_list.append(i.strip())

        if last_line.find(')') != -1:
          name = _ENUM_NAME_PATTERN2.search(last_line)
        else:
          name = _ENUM_NAME_PATTERN3.search(last_line)
        if name:
          name = name.group().split(',')
          for i in name:
            if i.strip() != '':
              if (not i.isupper()) or (i[-2:] != '_E'):
                _Error(filename, last_line_num, 'enum/name', 5,
                  'ENUM name format error.')
                break
              # struct_name_list.append(i.strip())

        # for name in struct_name_list:
        #   if (not name.isupper()) or (name[-2:] != '_E'):
        #     _Error(filename, raw_line_num, 'enum/name', 5,
        #       'ENUM name format error.')
        #     break


def GuardCheckForUnionRule(filename, file_extention, lines):
  for item in _UNION_CONSTRUCT_INFO:
    if item:
      for line_num in range(0, len(item['data'])):
        raw_line_num = line_num + item['line_start']
        line = item['data'][line_num]
        _CheckForCommonRules(filename, line, raw_line_num)


# def _FindOutIfElseifElseSentences(lines, start_line_num, stop_line_num):


def _FindoutMultiLineByGivedCharacter(lines, start_line_num, stop_line_num, newline_char) -> dict:
  """
  Args:
    lines: The list which contain all the line data to check
    start_line_num: The first line number which end with newline character.
    stop_line_num: The max line number we can search.
    newline_char: The newline character which we need to identify.
  Return -> dict:
    multi_line_info['count']: The line count (how many lines) we had found.
    multi_line_info['combo']: The combo of the multi line.
  """
  first_line = lines[start_line_num]
  combo_line = ''
  total_line_count = 0
  multi_line_info = {'count':0, 'combo':''}
  if not first_line.strip():
    return multi_line_info
  if first_line.strip()[-1] != '\\':
    return multi_line_info
  combo_line = first_line
  total_line_count += 1
  for line_num in range(start_line_num + 1, stop_line_num):
    total_line_count += 1
    line = lines[line_num]
    striped_line = line.strip()
    if striped_line:
      if striped_line[-1] == '\\':
        combo_line += striped_line.rstrip('\\')
      else:
        combo_line += striped_line
        break
  multi_line_info['count'] = total_line_count
  multi_line_info['combo'] = combo_line
  return multi_line_info


def _FindAllBracePairs(lines):
  """查找lines中的所有花括号对

  Args:
    lines: 需要处理的数据行
  Returrns:
    brace_list: 查找到的花括号对的行号, 起始行为0. 排列顺序为最小范围到最大范围
  
  example:
    lines:
      line0: {
      line1: {
      line2:  } {
      line3: }
      line4: }
    returns:
      [[1, 2], [2, 3], [0, 4]]
  """
  _BRACE_PATTERN = re.compile(r'[{}]')
  left_brace_list = []
  brace_list = []

  for line_num in range(0, len(lines)):
    line = lines[line_num]
    striped_line = line.strip()
    if striped_line:
      if _BRACE_PATTERN.search(line):
        for i in _BRACE_PATTERN.finditer(line):
          if i.group() == '{':
            left_brace_list.append(line_num)
          elif i.group() == '}':
            brace_list.append([left_brace_list.pop(), line_num])
  return brace_list


def _HandleAllMultiLineContext(lines):
  """将lines中的多行, 替换为单行
  example: 
    From:
      aaa, \
        bbb, \
        ccc \
    To:
      aaa,bbb,ccc
      blank
      blank

  Args: 
    lines: 需要处理的行数
  """
  for line_num in range(0, len(lines)):
    line = lines[line_num]
    striped_line = line.strip()
    if striped_line:
      if striped_line[-1] == '\\':
        info = _FindoutMultiLineByGivedCharacter(lines, line_num, len(lines), '\\')
        if info:
          lines[line_num] = info['combo']
          for i in range(line_num + 1, line_num + info['count']):
            lines[i] = ''

def GuardCheckForFuctionRule(filename, file_extention, lines):
  _INDENT_SPACE_COUNT = 4
  _PARENTHESIS_SQUAREBRACKETS_RULE_PATTERN = re.compile(r'\(\s|\s\)|\[\s|\s\]') # 圆括号和方括号,与内部字符之间，不应有空格

  _IF_PATTERN = re.compile(r'^\bif\b') # 匹配包含'if'的行
  _IF_RULE_PATTERN = re.compile(r'^if \(.*\) {$')
  _ELIF_PATTERN = re.compile(r'\belse\s*if\b')
  _ELIF_RULE_PATTERN = re.compile(r'^} else if \(.*\) {$')
  _ELSE_PATTERN = re.compile(r'\belse\b')
  _ELSE_RULE_PATTERN = re.compile(r'^} else {$')

  _FOR_PATTERN = re.compile(r'\bfor\b')
  _FOR_RULE_PATTERN = re.compile(r'^for \(.*\) {$')

  _SWITCH_PATTERN = re.compile(r'\bswitch\b')
  _SWITCH_RULE_PATTERN = re.compile(r'^switch \(.*\) {$')
  _SWITCH_CASE_PATTERN = re.compile(r'\bcase\b')
  _SWITCH_CASE_RULE_PATTERN = re.compile(r'^case \S.*\S:$')
  _SWITCH_BREAK_PATTERN = re.compile(r'\bbreak\b')
  _SWITCH_BREAK_RULE_PATTERN = re.compile(r'^break;$')
  _SWITCH_DEFAULT_PATTERN = re.compile(r'\bdefault\b')
  _SWITCH_DEFAULT_RULE_PATTERN = re.compile(r'^default:$')

  _COMMA_SEMICOLON_MID_PATTERN = re.compile(r'\S\s*[,;]\s*\S')
  _COMMA_SEMICOLON_MID_RULE_PATTERN = re.compile(r'\S[,;] \S')
  _COMMA_SEMICOLON_END_PATTERN = re.compile(r'\S\s*[,;]\s*$')
  _COMMA_SEMICOLON_END_RULE_PATTERN = re.compile(r'\S[,;]$')  

  _DOWHILE_DO_PATTERN = re.compile(r'\bdo\b')
  _DOWHILE_DO_RULE_PATTERN = re.compile(r'^do {$')
  _DOWHILE_WHILE_PATTERN = re.compile(r'\bwhile\b.*;$')
  _DOWHILE_WHILE_RULE_PATTERN = re.compile(r'^} while \(.*\);$')

  _WHILE_PATTERN = re.compile(r'^\s*while\s*(.*)\s*{?(?!;)\s*$')
  _WHILE_RULE_PATTERN = re.compile(r'^while \(.*\) {$')

  _BRACE_PATTERN = re.compile(r'[{}]')
  _BRACE_END_RULE_PATTERN = re.compile(r'^}$')

  for item in _FUNCTION_CONSTRUCT_INFO:
    if item:
      first_line_num = item['line_start']
      last_line_num = item['line_stop']
      current_indent_count = 0
      error_count = 0
      first_line = item['data'][0]
      brace_start_line_num = item['brackets_start']
      brace_start_line = item['data'][brace_start_line_num - first_line_num]
      last_line = item['data'][-1]
  
      # 函数的起始'{', 应位于函数名的下一行, 且应为独立行.
      if brace_start_line.strip() != '{':
        _Error(filename, brace_start_line_num, 'function/brace', 5,
                  'The "{" of the function should be at the next line.')
        error_count += 1
      # 函数的结束行'}', 应位于最后一行, 且应为独立行.
      if (last_line.strip() != '}'):
        _Error(filename, last_line_num, 'function/brace', 5,
                  'The "}" of the fuction should at the last line.')
        error_count += 1

      # for line_num in range(0, len(item['data'])):
      line_num = 0
      while line_num < len(item['data']):
        raw_line_num = line_num + item['line_start']
        line = item['data'][line_num]
        striped_line = line.strip()

        # 检查'()' '[]'的规则
        if _PARENTHESIS_SQUAREBRACKETS_RULE_PATTERN.search(line):
          _Error(filename, raw_line_num, 'function/brackets', 5,
            'In brackets, no space after "(" "[", no space in front of ")" "]"')
          error_count += 1

        if striped_line:
          # 检查'if' 'else if' 'else' 单行语法
          if _ELIF_PATTERN.search(striped_line):
            if not _ELIF_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/if_else', 5,
                'Format error, should be: "} else if (xxx) {".')
              error_count += 1
          elif _IF_PATTERN.search(striped_line):
            # skip_check = 0
            temp_line = striped_line
            if striped_line[-1] == '\\':
              info = _FindoutMultiLineByGivedCharacter(item['data'], line_num, item['line_stop'], '\\')
              temp_line = info['combo']
              # line_num += info['count']
              # skip_check = 1
            if not _IF_RULE_PATTERN.search(temp_line):
              _Error(filename, raw_line_num, 'function/if_else', 5,
                'Format error, should be: "if (xxx) {".')
              error_count += 1
            # if skip_check == 1:
            #   continue
          elif _ELSE_PATTERN.search(striped_line):
            if not _ELSE_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/if_else', 5,
                'Format error, should be: "} else {".')
              error_count += 1
          
          # 检查 'for'单行语法
          if _FOR_PATTERN.search(striped_line):
            if not _FOR_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/for', 5,
                'Format error, should be: "for (xxx) {".')
              error_count += 1
          
          # 检查 'switch' 'case' 'break' 'default'单行语法
          if _SWITCH_PATTERN.search(striped_line):
            if not _SWITCH_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/switch_case', 5,
                'Format error, should be: "switch (xxx) {".')
              error_count += 1
          elif _SWITCH_CASE_PATTERN.search(striped_line):
            if not _SWITCH_CASE_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/switch_case', 5,
                'Format error, should be: "case xxx :".')
              error_count += 1
          elif _SWITCH_BREAK_PATTERN.search(striped_line):
            if not _SWITCH_BREAK_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/switch_case', 5,
                'Format error, should be: "break;".')
              error_count += 1
          elif _SWITCH_DEFAULT_PATTERN.search(striped_line):
            if not _SWITCH_DEFAULT_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/switch_case', 5,
                'Format error, should be: "default:".')

          # 检查 ',' ';'
          if _COMMA_SEMICOLON_MID_PATTERN.search(striped_line):
            for i in _COMMA_SEMICOLON_MID_PATTERN.finditer(striped_line):
              if not _COMMA_SEMICOLON_MID_RULE_PATTERN.search(i.group()):
                _Error(filename, raw_line_num, 'function/comma_semicolon', 5,
                  'Format error, the "," or ";" which at the middle of line, should be "A, B" or "A; B"')
                error_count += 1
                break
          elif _COMMA_SEMICOLON_END_PATTERN.search(striped_line):
            for i in _COMMA_SEMICOLON_END_PATTERN.finditer(striped_line):
              if not _COMMA_SEMICOLON_END_RULE_PATTERN.search(i.group()):
                _Error(filename, raw_line_num, 'function/comma_semicolon', 5,
                  'Format error, the "," or ";" which at the end of line, lineshould be "A," or "A;"')
                error_count += 1
                break

          # 检查'do-while' 'while' 单行语法
          if _DOWHILE_DO_PATTERN.search(striped_line):
            if not _DOWHILE_DO_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/do_while', 5,
                'Format error, should be "do {".')
              error_count += 1
          elif _DOWHILE_WHILE_PATTERN.search(striped_line):
            if not _DOWHILE_WHILE_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/do_while', 5,
                'Format error, should be "} while (xxx);".')
              error_count += 1
          elif _WHILE_PATTERN.search(striped_line):
            if not _WHILE_RULE_PATTERN.search(striped_line):
              _Error(filename, raw_line_num, 'function/do_while', 5,
                'Format error, should be "while (xxx) {".')
              error_count += 1

        # 检查缩进
        # copied_lines = item['data']
        # _HandleAllMultiLineContext(copied_lines)
        # brace_list = _FindAllBracePairs(item['data'])

        # for brace_pair in brace_list:
        #   start_line_num = brace_pair[0]
        #   stop_line_num = brace_pair[1]
        #   start_line = copied_lines[brace_pair[0]]
        #   stop_line = copied_lines[brace_pair[1]]
        #   if 

        # in_multi_line_context = False
        # brace_depth = 0
        # in_switch_case_context = False
        # current_indent_count = _INDENT_SPACE_COUNT
        # next_indent_count = _INDENT_SPACE_COUNT
        # if (error_count == 0) and striped_line and (line_num > brace_start_line_num):
        #   if striped_line[-1] == '\\':
        #     next_indent_count = current_indent_count + _INDENT_SPACE_COUNT
        #     in_multi_line_context = True
        #   elif in_multi_line_context == True:
        #     in_multi_line_context = False
        #   if striped_line[-1] == '{':
        #     next_indent_count = current_indent_count + _INDENT_SPACE_COUNT
        #     brace_depth += 1
        #     if _SWITCH_CASE_RULE_PATTERN.match(striped_line):
        #       in_switch_case_context = True
        #   elif _ELIF_RULE_PATTERN.match(striped_line):
        #     next_indent_count = current_indent_count + _INDENT_SPACE_COUNT
        #   elif _ELSE_RULE_PATTERN.match(striped_line):
        #     next_indent_count = current_indent_count + _INDENT_SPACE_COUNT
        #   elif striped_line == '}':
        #     brace_depth -= 1
        #     next_indent_count = current_indent_count - _INDENT_SPACE_COUNT
        #   elif _DOWHILE_WHILE_RULE_PATTERN.match(striped_line):
        #     next_indent_count = current_indent_count - _INDENT_SPACE_COUNT

        #   if _CountLeftSpace(line) != current_indent_count:
        #     _Error(filename, raw_line_num, 'function/indentation', 5,
        #       'The indentation for this line should be %i space, but you have %i' % (current_indent_count, _CountLeftSpace(line)))
        #   current_indent_count = next_indent_count
            
        # 当本行最后为'}'时，缩进减1
        # if (error_count == 0) and striped_line:
        #   if striped_line[0] == '}':
        #     current_indent_count -= _INDENT_SPACE_COUNT
        #   temp_indent = 0
        #   if raw_line_num > first_line_num:
        #     previous_line = item['data'][line_num - 1]
        #     striped_previous_line = previous_line.strip()
        #     if striped_previous_line:
        #       if striped_previous_line[-1] == '\\':
        #         temp_indent = _INDENT_SPACE_COUNT
        #   if (_CountLeftSpace(line) != current_indent_count + temp_indent) and striped_line:
        #     _Error(filename, raw_line_num, 'function/indentation', 5,
        #       'The indentation for this line should be %i space, but you have %i' % (current_indent_count + temp_indent, _CountLeftSpace(line)))

        #   if striped_line[-1] == '{':
        #     current_indent_count += _INDENT_SPACE_COUNT
          # if _SWITCH_CASE_PATTERN.search(striped_line):
          #   current_indent_count += _INDENT_SPACE_COUNT
          # elif _SWITCH_BREAK_PATTERN.search(striped_line):
          #   current_indent_count -= _INDENT_SPACE_COUNT
          # elif _SWITCH_DEFAULT_PATTERN.search(striped_line):
          #   current_indent_count += _INDENT_SPACE_COUNT

        line_num += 1 # should be the last operation
        # 局部变量驼峰格式
        # 函数名驼峰格式
        # 函数行数
        # 函数名长度
        # 检查参数个数
        # if, for, while, switch, do-while
        # for语句中的';'


def GuardCheckForHashDefineRule(filename, file_extention, lines):
  """
  1. 多行模式的缩进
  2. 换行符与前面的空格数量
  3. 最后一行不应有换行符
  """
  pass


"""运算符优先级
  优先级    运算符    名称或含义          使用形式                    结合方向      说明
  -----     ----     ---------          -------                   ---------     -----
    1        []       数组下标          数组名[常量表达式]            左到右
    1        ()       圆括号            (表达式)/函数名(形参表)       左到右
    1        .        成员选择(指针)    对象.成员名                   左到右
    1        ->       成员选择(指针)    对象指针->成员名              左到右

    2        -        负号              -表达式                      右到左       单目 <<<<<<<<
    2        (类型)   强制类型转换      (数据类型)表达式              右到左        单目
    2        ++       自增             ++变量名/变量名++             右到左        单目 <<<<<<<<
    2        --       自减             --变量名/变量名--             右到左        单目 <<<<<<<<
    2        *        取值             *指针变量                    右到左         单目 <<<<<<<<
    2        &        取地址           &变量名                       右到左        单目 <<<<<<<<
    2        !        逻辑非           !表达式                       右到左        单目 !!
    2        ~        按位取反         ~表达式                       右到左        单目
    2        sizeof   长度运算         sizeof(表达式)                右到左        单目

    3        /        除               表达式 / 表达式               左到右        双目
    3        *        乘               表达式 * 表达式               左到右        双目 <<<<<<<<
    3        %        取余             整形表达式 % 整形表达式        左到右        双目

    4        +        加               表达式 + 表达式               左到右        双目 <<<<<<<<
    4        -        减               表达式 - 表达式               左到右        双目 <<<<<<<<

    5        <<       左移             变量 << 表达式                左到右        双目
    5        >>       右移             变量 >> 表达式                左到右        双目

    6        >        大于             表达式 > 表达式               左到右        双目
    6        >=       大于等于         表达式 >= 表达式               左到右        双目
    6        <        小于             表达式 < 表达式               左到右        双目
    6        <=       小于等于         表达式 <= 表达式              左到右        双目

    7        ==       等于             表达式 == 表达式              左到右        双目
    7        !=       不等于           表达式 != 表达式              左到右        双目

    8        &        按位与           表达式 & 表达式               左到右        双目 <<<<<<<<<

    9        ^        按位异或         表达式 ^ 表达式               左到右        双目

    10       |        按位或           表达式 | 表达式               左到右        双目

    11       &&       逻辑与           表达式 && 表达式               左到右        双目

    12       ||       逻辑或           表达式 || 表达式               左到右        双目

    13       ?:       条件运算符        表达式? 表达式 : 表达式       右到左         三目 <<<<<<<<<

    14       =        赋值             变量 = 表达式                  右到左        双目
    14       /=       除后赋值          变量 /= 表达式                右到左        双目
    14       *=       乘后赋值          变量 *= 表达式                右到左        双目
    14       %=       取模后赋值        变量 %= 表达式                右到左        双目
    14       +=       加后赋值          变量 += 表达式                右到左        双目
    14       -=       减后赋值          变量 -= 表达式                右到左        双目
    14       <<=      左移后赋值        变量 <<= 表达式               右到左        双目
    14       >>=      右移后赋值        变量 >>= 表达式               右到左        双目
    14       &=       按位与后赋值      变量 &= 表达式                右到左        双目
    14       ^=       按位异或后赋值    变量 ^= 表达式                右到左        双目
    14       |=       按位或后赋值      变量 |= 表达式                右到左        双目

    15       ,        逗号运算符        表达式,  表达式                左到右        从左向右顺序运算
"""
# def _GuardCheckForBiaryOperatorSyntax(filename, file_extention, lines):
#   """TODO: 检查双目运算符格式 (注释已删除, 已排除注释中逗号的影响)
#   1. 排除字符串影响
#   2. 单符号和多符号组合
#   3. '+', '-', '/', '%', '<<', '>>', '<', '>', '<=', '>=', '==', '!=', '|', ';', '&&', '||', 
#     '=', '*=', '/=', '%=', '+=', '-=', '<<=', '>>=', '&=', '^=', '|=', '<:', ':>', '<%', '%>', '%:', '%:%:'
#   4. '->', '.'
#   5. 三目运算符'? :'

#   Args:
#     filename: 需要处理的文件名.
#     file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
#     lines: filename中的所有行列表[].
#   """
#   _BIARY_SYNBLE_OPERATEOR_PATTERN = re.compile(r'/|%|\+|-|<<|>>|>|>=|<|<=|==|!=')
#   for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
#     if _BIARY_SYNBLE_OPERATEOR_PATTERN.search(lines[line_num]):
#       print(lines[line_num])



# def _GuardCheckForUnaryOperatorSyntax(filename, file_extention, lines):
#   """TODO: 检查单目运算符格式(注释已删除, 已排除注释中逗号的影响)
#     2        -        负号              -表达式                      右到左       单目 <<<<<<<< i--, i-=1, i = a-b 
#     2        (类型)   强制类型转换      (数据类型)表达式              右到左        单目
#     2        ++       自增             ++变量名/变量名++             右到左        单目
#     2        --       自减             --变量名/变量名--             右到左        单目
#     2        *        取值             *指针变量                    右到左         单目 <<<<<<<<
#     2        &        取地址           &变量名                       右到左        单目 <<<<<<<<
#     2        !        逻辑非           !表达式                       右到左        单目
#     2        ~        按位取反         ~表达式                       右到左        单目
#     2        sizeof   长度运算         sizeof(表达式)                右到左        单目

#   1. '++'不能与'+'在同一行, 同理'--'
#   2. 

#   Args:
#     filename: 需要处理的文件名.
#     file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
#     lines: filename中的所有行列表[].
#   """
#   _VARIABLE_PATTERN = r'[a-zA-Z0-9]+\b'
#   # 此处字符串已替换, 排除了字符串的影响. 
#   for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
#     pass


# def _GuardCheckForCommaSyntax(filename, file_extention, lines):
#   """TODO: 检查逗号规范, 应符合'字符串,空格 ' (注释已删除, 已排除注释中逗号的影响)
#   1. 排除enum影响
#   2. 排除字符串影响
#   3. 排除结构提数组的影响
#   4. 是否允许结构体数组为对齐美观而留下的空格

#   Args:
#     filename: 需要处理的文件名.
#     file_extention: filename的文件扩展类型, 不含'.'符号, 如'c', 'h'等.
#     lines: filename中的所有行列表[].
#   """
#   _COMMA_PATTERN = re.compile(r',')
#   _COMMA_SEARCH_ALL_PATTERN = re.compile(r'\S\s*,\s*\S')
#   _COMMA_RULE_PATTERN = re.compile(r'^\S, \S$')
#   for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
#     if _COMMA_PATTERN.search(lines[line_num]): # 此行有逗号
#       for iter in _COMMA_SEARCH_ALL_PATTERN.finditer(lines[line_num]):
#         if not _COMMA_RULE_PATTERN.match(iter.group()):
#           print(lines[line_num])
#           continue


def _ProcessFileLines(filename, file_extention, lines):
  # 常规检查, 与注释, 操作符无冲突
  GuardCheckForFilenameFormat(filename, file_extention, lines) # 检查文件命名规范
  # GuardCheckForCopyright(filename, file_extention, lines) # 检查是否包含copyright
  GuardCheckForNewlineAtEndOfFile(filename, file_extention, lines) # 检查文件最后一行应为空行
  GuardCheckForLineLength(filename, file_extention, lines) # 检查每一行的长度(包含空格)
  GuardCheckForLineEndingWithBlank(filename, file_extention, lines) # 检查行尾是否有多余空白
  GuardCheckForTabInLine(filename, file_extention, lines) # 检查文件中是否有tab符号
  GuardCheckForLanguage(filename, file_extention, lines) # 文件中不应包含中文字符
  GuardCheckForIncludeInHeaderFile(filename, file_extention, lines) # 头文件中不能有#include
  GuardCheckForHeaderFileHaveMarcoProtect(filename, file_extention, lines) # 头文件应使用#ifdef _FILE_NAME_H_包含
  GuardCheckForSrcFileUseExtern(filename, file_extention, lines) # .c文件中不应使用extern

  # print('test')
  # 注释检查会将注释替换成空白
  # 部分规范检查需要在此之前完成, 如空行检查, 行尾空白等
  #部分规范检查需要在此后完成, 如construct分析, 运算符检查等. 
  GuardCheckForSingleLineCommentAndReplaceByBlank(filename, file_extention, lines) # 检查"//"注释规范 must
  GuardCheckForMultiLineCommentAndReplaceByBlank(filename, file_extention, lines) # 检查"/**/"注释规范 must

  # 常规检查, 与注释, 操作符无冲突, 但需要替代成空白或特殊字符串的
  # 由于这些操作符后, 可能存在注释, 因此将此项常规检查放在注释检查之后
  GuardCheckForHashIfFormatAndReplaceWithBlankLine(filename, file_extention, lines) # 检查 #if等宏是否顶行书写
  GuardCheckForHashDefineFormatAndReplaceWithBlank(filename, file_extention, lines) # 检查 #define等宏是否顶行书写
  GuardCheckForIncludeFormat(filename, file_extention, lines) # 检查#include格式

  # 此处会将字符和字符串, 替换为特定字符_STRING
  # 由于字符串中含有运算符, 因此所有特殊字符, 因此运算符检查需要在此之后进行
  _FindoutStringAndReplaceWithSpecialSymbol(filename, file_extention, lines)

  # 此函数会找到所有的enum/function/struct/union等原始定义, 并将lines中对应位置替换为空白, 并保存constructs信息到对应列表. 
  # 由于很多运算符在不同结构中的使用方法不一样, 因此运算符检查需要使用此处生成的constructs list来检查. 
  _FindoutAllMultiLineBLock(filename, file_extention, lines)
  # print('test')

  # 所有的复杂结构, 已经被拿到单独的list中检查, 剩下的主要是变量定义, 声明等信息
  # GuardCheckForRestOfTheLines(filename, file_extention, lines)
  
  GuardCheckForStructRule(filename, file_extention, lines)
  GuardCheckForEnumRule(filename, file_extention, lines)
  GuardCheckForUnionRule(filename, file_extention, lines)
  GuardCheckForFuctionRule(filename, file_extention, lines)
  GuardCheckForHashDefineRule(filename, file_extention, lines)
  # _GuardCheckForCommaSyntax(filename, file_extention, lines)
  # GuardCheckForInvalidIncrement(filename, file_extention, lines) # 检查无效的自增运算*p++
  # _GuardCheckForBiaryOperatorSyntax(filename, file_extention, lines)

  # print('test')
  """
  # TODO:
  函数:
    1. 函数名长度限制
    2. 参数个数限制
    3. 行数限制
    4. 函数是否有注释
    5. 函数命名
    6. 函数超长
  变量
    1. 全局变量命名
    2. 局部变量命名
    3. 局部数组变量超长
    4. 变量类型,如UINT_8, 是否为kernel_type.h中的定义
    5. 指针变量
  Log:
    1. 字符串数量限制
    2. __func__, __line__宏限制
  switch
    1. 冒号规则
    2. case规则
    3. break规则
  其他:
    1. 提示malloc, realloc, alloc
    2. 提示memcpy, strcpy 等
    3. 缩进
  """

  # GuardCheckForCommaSyntax(filename, file_extention, lines) # 检查逗号规范"\S, "
  # fd = open(filename + '_parsed', 'w', encoding='utf8')
  # for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
  #   fd.write(lines[line_num] + '\n')
  # fd.close()


  pass


def ProcessFile(filename):
  """Does coding style check on a single file.
  
  Args:
    filename: The name of the file to check.
  """
  filename = filename.strip()
  file_extention = filename[filename.rfind('.') + 1:]
  if file_extention not in  _valid_extentions:
    print('Unsupported file extention:' + '[' + filename +']')
    return

  try:
    lines = open(filename, mode='r', encoding='utf8').read().split("\n")
  except:
    print("Could not open file:" + filename)
    return

  if _CheckFileNotEmpty is False:
    print('File is empty, no need check:' + filename)
    return

  lines = (['// marker so line numbers and indices both start at 1'] + lines +
           ['// marker so line numbers end in a known way'])
 
  global _LINE_LENS_RANGE_START
  global _LINE_LENS_RANGE_STOP
  global _LINE_LENS_TRUE
  global _LINE_LENS_WITH_MARKER

  _LINE_LENS_RANGE_START = 1
  _LINE_LENS_RANGE_STOP = len(lines) -1
  _LINE_LENS_TRUE = len(lines) - 2
  _LINE_LENS_WITH_MARKER = len(lines)

  _ProcessFileLines(filename, file_extention, lines)


def main():
  filenames = sys.argv[1:]
  if _test_files:
    filenames = _test_files

  if not filenames:
    _PrintUsage('No file specified')

  _clint_state.ResetErrorCounts()

  for filename in filenames:
      ProcessFile(filename)

  # Print the error counts
  sys.exit(_clint_state.error_count > 0)

if __name__ == '__main__':
    main()
